package com.whfc.fuum.service.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.whfc.common.enums.EnableState;
import com.whfc.common.enums.ExpireFlag;
import com.whfc.common.enums.SysRulePlatform;
import com.whfc.common.exception.BizException;
import com.whfc.common.result.PageData;
import com.whfc.common.result.ResultEnum;
import com.whfc.common.util.JwtUtils;
import com.whfc.common.util.MD5Util;
import com.whfc.common.util.PageUtil;
import com.whfc.common.util.SignUtil;
import com.whfc.fuum.dao.*;
import com.whfc.fuum.dto.SysDeptDTO;
import com.whfc.fuum.dto.SysRuleDTO;
import com.whfc.fuum.dto.open.AccessCodeDTO;
import com.whfc.fuum.dto.open.AccessTokenDTO;
import com.whfc.fuum.dto.open.OpenApiClientDTO;
import com.whfc.fuum.dto.open.OpenApiLicenseDTO;
import com.whfc.fuum.entity.OpenApiLicense;
import com.whfc.fuum.entity.OpenApiToken;
import com.whfc.fuum.entity.SysUser;
import com.whfc.fuum.entity.SysUserCode;
import com.whfc.fuum.param.open.AccessTokenParam;
import com.whfc.fuum.param.open.OpenApiRuleEditParam;
import com.whfc.fuum.param.open.OpenApiTokenParam;
import com.whfc.fuum.service.OpenApiService;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;

import java.util.*;

/**
 * @Description:
 * @author: xugcheng
 * @version: 1.0
 * @date: 2020-12-29 9:36
 */
@DubboService(interfaceClass = OpenApiService.class, version = "1.0.0")
public class OpenApiServiceImpl implements OpenApiService {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    private static final long TOKEN_EXPIRES_IN_SECONDS = 24 * 3600;

    private static final long SIGN_EXPIRES_IN_MILLS = 10 * 60 * 1000;

    private static final long OPEN_API_CODE_IN_SECONDS = 2 * 60;

    private static final long REFRESH_TIME_IN_MILLS = 10 * 60 * 1000;

    @Autowired
    private OpenApiLicenseMapper openApiLicenseMapper;

    @Autowired
    private OpenApiTokenMapper openApiTokenMapper;

    @Autowired
    private OpenApiLicenseRuleMapper openApiLicenseRuleMapper;

    @Autowired
    private SysUserMapper sysUserMapper;

    @Autowired
    private SysDeptMapper sysDeptMapper;

    @Autowired
    private SysRuleMapper sysRuleMapper;

    @Autowired
    private SysUserCodeMapper sysUserCodeMapper;

    @Override
    public AccessTokenDTO getAccessToken(AccessTokenParam accessTokenParam) throws BizException {
        String appKey = accessTokenParam.getAppId();
        Long timestamp = accessTokenParam.getTimestamp();
        String randomStr = accessTokenParam.getRandomStr();
        String sign = accessTokenParam.getSign();

        //验证appId
        OpenApiLicense license = openApiLicenseMapper.selectByAppKey(appKey);
        if (license == null) {
            throw new BizException(ResultEnum.NOT_FOUND.getCode(), "appId不正确");
        }
        //验证时间戳
        if (System.currentTimeMillis() - timestamp > SIGN_EXPIRES_IN_MILLS) {
            throw new BizException(ResultEnum.FAILURE.getCode(), "签名timestamp已过期");
        }

        //验证签名
        String calSign = SignUtil.sign(appKey, timestamp, randomStr, license.getAppSecret());
        logger.info("sign:[{}],calSign:[{}]", sign, calSign);
        if (!sign.equalsIgnoreCase(calSign)) {
            throw new BizException(ResultEnum.FAILURE.getCode(), "sign不正确");
        }

        //生成accessToken
        Integer deptId = license.getDeptId();
        String accessToken = UUID.randomUUID().toString().replace("-", "");
        Date now = new Date();
        long expiresIn = TOKEN_EXPIRES_IN_SECONDS;
        Date expireTime = new Date(now.getTime() + TOKEN_EXPIRES_IN_SECONDS * 1000);

        OpenApiToken token = new OpenApiToken();
        token.setDeptId(deptId);
        token.setAppKey(appKey);
        token.setAccessToken(accessToken);
        token.setExpiresIn(expiresIn);
        token.setExpireTime(expireTime);
        token.setCreateTime(now);
        openApiTokenMapper.insertSelective(token);

        //返回值
        AccessTokenDTO tokenDTO = new AccessTokenDTO();
        tokenDTO.setAccessToken(accessToken);
        tokenDTO.setExpireIn(expiresIn);
        tokenDTO.setCreateTime(now);
        return tokenDTO;
    }

    @Override
    public AccessTokenDTO getOpenApiToken(OpenApiTokenParam param) throws BizException {
        String appKey = param.getAppKey();
        Long timestamp = param.getTimestamp();
        String sign = param.getSign();
        String randomStr = param.getRandomStr();

        //验证时间戳
        if (System.currentTimeMillis() - timestamp > SIGN_EXPIRES_IN_MILLS) {
            throw new BizException(ResultEnum.FAILURE.getCode(), "签名timestamp已过期");
        }
        //验证appKey
        OpenApiLicense license = openApiLicenseMapper.selectByAppKey(appKey);
        if (license == null) {
            throw new BizException(ResultEnum.NOT_FOUND.getCode(), "appkey不正确");
        }

        //验证签名
        String calSign = SignUtil.signV1(appKey, timestamp, randomStr, license.getAppSecret());
        logger.info("sign:[{}],calSign:[{}]", sign, calSign);
        if (!sign.equalsIgnoreCase(calSign)) {
            throw new BizException(ResultEnum.FAILURE.getCode(), "sign不正确");
        }

        //accessToken
        Date now = new Date();
        Integer deptId = license.getDeptId();
        AccessTokenDTO tokenDTO = openApiTokenMapper.selectByDeptId(deptId, now);
        if (ObjectUtils.isEmpty(tokenDTO) || tokenDTO.getExpireTime().getTime() - now.getTime() < REFRESH_TIME_IN_MILLS) {
            //生成accessToken
            String accessToken = UUID.randomUUID().toString().replace("-", "");

            long expiresIn = TOKEN_EXPIRES_IN_SECONDS;
            Date expireTime = new Date(now.getTime() + TOKEN_EXPIRES_IN_SECONDS * 1000);

            OpenApiToken token = new OpenApiToken();
            token.setDeptId(deptId);
            token.setAppKey(appKey);
            token.setAccessToken(accessToken);
            token.setExpiresIn(expiresIn);
            token.setExpireTime(expireTime);
            token.setCreateTime(now);
            openApiTokenMapper.insertSelective(token);

            tokenDTO = new AccessTokenDTO();
            tokenDTO.setAccessToken(accessToken);
            tokenDTO.setExpireIn(expiresIn);
            tokenDTO.setExpireTime(expireTime);
        }

        return tokenDTO;
    }


    @Override
    public OpenApiClientDTO validateOpenApiToken(String accessToken) throws BizException {
        //验证token
        OpenApiToken token = openApiTokenMapper.selectByAccessToken(accessToken);
        if (token == null) {
            throw new BizException(ResultEnum.FAILURE.getCode(), "accessToken无效");
        }
        if (System.currentTimeMillis() > token.getExpireTime().getTime()) {
            throw new BizException(ResultEnum.FAILURE.getCode(), "accessToken已过期");
        }
        OpenApiClientDTO openApiClientDTO = new OpenApiClientDTO();
        openApiClientDTO.setDeptId(token.getDeptId());
        openApiClientDTO.setAppKey(token.getAppKey());
        return openApiClientDTO;
    }

    @Override
    public AccessCodeDTO getCode(Integer deptId, String username, String phone) throws BizException {
        //验证用户名
        SysUser sysUser = sysUserMapper.selectByUsername(username);
        if (sysUser == null) {
            throw new BizException(ResultEnum.FAILURE.getCode(), "用户名错误");
        }
        //验证手机号
        if (!phone.equals(sysUser.getPhone())) {
            throw new BizException(ResultEnum.FAILURE.getCode(), "电话号码错误");
        }
        //验证手机号与用户匹配
        boolean isValid = false;
        Integer userId = sysUser.getId();

        List<SysDeptDTO> deptDTOList = sysDeptMapper.selectProjectListByUserId(userId);
        for (SysDeptDTO deptDTO : deptDTOList) {
            if (deptDTO.getId().equals(deptId)) {
                isValid = true;
                break;
            }
        }
        if (!isValid) {
            throw new BizException(ResultEnum.FAILURE.getCode(), "没有登录此账号权限");
        }

        // 验证通过，生成code
        String code = MD5Util.md5Encode(System.currentTimeMillis() + deptId + username + phone);
        Date expireTime = new Date(System.currentTimeMillis() + OPEN_API_CODE_IN_SECONDS * 1000);
        SysUserCode record = new SysUserCode();
        record.setCode(code);
        record.setExpiresIn(OPEN_API_CODE_IN_SECONDS);
        record.setExpireTime(expireTime);
        record.setUsername(username);
        record.setExpireFlag(ExpireFlag.UNEXPIRED.getValue());

        sysUserCodeMapper.insertSelective(record);

        AccessCodeDTO data = new AccessCodeDTO();
        data.setCode(code);
        return data;
    }

    @Override
    public PageData<OpenApiLicenseDTO> getOpenApiLicenseList(Integer pageNum, Integer pageSize, String keyword) throws BizException {
        PageHelper.startPage(pageNum, pageSize);
        List<OpenApiLicenseDTO> list = openApiLicenseMapper.selectOpenApiLicenseDTOList(keyword);
        PageHelper.clearPage();
        return PageUtil.pageData(PageInfo.of(list));
    }


    @Override
    public List<OpenApiLicenseDTO> getOpenApiLicenseList(Integer deptId) throws BizException {
        return openApiLicenseMapper.selectLicenseByDeptId(deptId);
    }

    @Override
    public List<SysRuleDTO> getOpenApiRules() throws BizException {
        return sysRuleMapper.selectAllRuleList(SysRulePlatform.OPEN.getValue());
    }


    @Override
    public List<SysRuleDTO> getOpenApiRules(String appKey) throws BizException {
        List<Integer> ruleIdList = openApiLicenseRuleMapper.selectRuleIdListByAppKey(appKey);
        if (ruleIdList.size() > 0) {
            return sysRuleMapper.selectRuleListByRuleIdList(ruleIdList);
        }
        return Collections.emptyList();
    }

    @Override
    public List<SysRuleDTO> getOpenApiRulesByDeptId(Integer deptId) throws BizException {
        List<Integer> ruleIdList = openApiLicenseRuleMapper.selectRuleIdListByDeptId(deptId);
        if (ruleIdList.size() > 0) {
            return sysRuleMapper.selectRuleListByRuleIdList(ruleIdList);
        }
        return Collections.emptyList();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void editOpenApiRules(OpenApiRuleEditParam param) throws BizException {
        String appKey = param.getAppKey();
        Integer deptId = param.getDeptId();
        List<Integer> ruleIdList = param.getRuleIdList();
        if (StringUtils.isNotBlank(appKey)) {
            openApiLicenseRuleMapper.deleteByAppKey(appKey);
            if (!ruleIdList.isEmpty()) {
                openApiLicenseRuleMapper.batchInsert(null, appKey, ruleIdList);
            }
        } else if (deptId != null) {
            openApiLicenseRuleMapper.deleteByDeptId(deptId);
            if (!ruleIdList.isEmpty()) {
                openApiLicenseRuleMapper.batchInsert(deptId, null, ruleIdList);
            }
        }
    }

    @Override
    public void enableOpenApiLicense(String appKey) throws BizException {
        openApiLicenseMapper.enableByAppKey(appKey);
    }

    @Override
    public void disableOpenApiLicense(String appKey) throws BizException {
        openApiLicenseMapper.disableByAppKey(appKey);
    }

    @Override
    public Integer getDeptIdByAppKey(String appKey) throws BizException {
        logger.info("通过appKey获取deptId服务,appKey:{}", appKey);
        OpenApiLicense openApiLicense = openApiLicenseMapper.selectByAppKey(appKey);
        if (openApiLicense == null || EnableState.DISABLED.getValue().equals(openApiLicense.getState())) {
            throw new BizException(ResultEnum.NOT_FOUND.getCode(), "appkey不正确");
        }
        return openApiLicense.getDeptId();
    }

    @Override
    public OpenApiLicenseDTO getOpenApiLicense(String appKey) throws BizException {
        OpenApiLicense openApiLicense = openApiLicenseMapper.selectByAppKey(appKey);
        if (openApiLicense != null) {
            OpenApiLicenseDTO openApiLicenseDTO = new OpenApiLicenseDTO();
            BeanUtils.copyProperties(openApiLicense, openApiLicenseDTO);
            return openApiLicenseDTO;
        }
        return null;
    }

    @Override
    public void configureOpenApiLicense(Integer deptId, List<OpenApiLicenseDTO> list) throws BizException {
        if (deptId == null) {
            throw new BizException(ResultEnum.PARAM_ERROR.getCode(), "组织机构ID不能为空.");
        }
        //删除当前组织机构的license
        openApiLicenseMapper.logicDelByDeptId(deptId);
        //保存新的license
        for (OpenApiLicenseDTO openApiLicenseDTO : list) {
            OpenApiLicense license = new OpenApiLicense();
            license.setDeptId(deptId);
            license.setAppKey(openApiLicenseDTO.getAppKey());
            license.setAppSecret(openApiLicenseDTO.getAppSecret());
            license.setRemark(openApiLicenseDTO.getRemark());
            openApiLicenseMapper.insertSelective(license);
        }

    }


    @Override
    public String getQiuyanAccessToken(String apiKey, String apiSecret) throws BizException {
        OpenApiLicense license = openApiLicenseMapper.selectLicense(apiKey, apiSecret);
        if (license == null) {
            throw new BizException(ResultEnum.AUTHZ_ERROR.getCode(), "该账号未授权");
        }
        Integer deptId = license.getDeptId();
        //设置过期时间为7天
        long expire = 7 * 24 * 60 * 60 * 1000;
        Map<String, Object> map = new HashMap<>();
        map.put("deptId", deptId);
        return JwtUtils.createJwt(expire, apiKey, apiSecret, map);
    }


}
